#include "CentralCache.h"
#include "PageCache.h"
#include "Common.h"

//CentralCache CentralCache::_sInst;
CentralCache CentralCache::_sInst;

// 获取一个非空的span
Span* CentralCache::GetOneSpan(SpanList& list, size_t size) //此时的size已经是aligSize，可按此对齐/切分
{
	// 在list中寻找非空Span，没有则从Page中取Span后挂到list上并返回Span
	Span* it = list.Begin();
	while (it != list.End())
	{
		if (it->_freeList != nullptr) return it;
		it = it->_next;
	}
	// 先把central cache的桶锁解掉，这样如果其他线程释放内存对象回来，不会阻塞
	list._mtx.unlock();// 解的锁是FetchRangeObj中加的锁

	PageCache::GetInstance()->_pageMtx.lock();
	Span* span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));
	PageCache::GetInstance()->_pageMtx.unlock();
	
	//切分Span，不需加锁，因为这是新生Span，此时只会在这一个线程中被访问；
	// 这是对span的第二层初始化，在此之前span已在PageCache::NewSpan()中完成第一次初始化（此时_n, _pageId均已初始化）
	char* start = (char*)(span->_pageId << PAGE_SHIFT);
	char* end = start + (span->_n << PAGE_SHIFT);
	span->_freeList = start;
	//size_t useCount = 0;
	// 开始切分span到_freelist
	while (start < end)
	{
		//useCount++;
		NextObj(start) = (void*)(start + size);
		start = (char*)NextObj(start);
	}
	NextObj(end - size) = nullptr;//确保_freelist以nullptr结尾

	//span->_useCount = useCount;
	list._mtx.lock();// 这个锁会在FetchRangeObj中解除
	list.PushFront(span);

	// list是否unlock？否，但是在CentralCache::FetchRangeObj()中unlock
	// _freelist末尾是否nullptr? 必须是nullptr
	// span->_useCount? 只是切分，并没有使用，所以useCount=0
	return span;
}

size_t CentralCache::FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size)// 获取batchNum个大小为size的空间,[start,end]均有效
{
	size_t index = SizeClass::Index(size);
	_spanLists[index]._mtx.lock();
	Span* span = GetOneSpan(_spanLists[index], size);
	assert(span);
	assert(span->_freeList);

	// 从span中获取batchNum个对象
	// 如果不够batchNum个， 有多少拿多少
	start = span->_freeList;
	end = start;
	size_t actualNum = 1;
	while (actualNum < batchNum 
		&& 
		NextObj(end) != nullptr)
	{
		end = NextObj(end);
		actualNum++;
	}

	span->_freeList = NextObj(end);
	
	NextObj(end) = nullptr;

	_spanLists[index]._mtx.unlock();
	return actualNum;
}